package com.roy.minimq.remoting.domain.entity;

import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author ：楼兰
 * @date ：Created in 2021/8/9
 * Netty请求自定义传输协议，比RocketMQ的序列化协议简化了不少。
 * RocketMQ的指令有两个自定义参数，分为header和body两部分，header使用自定义序列化协议，body默认使用fastjson序列化。
 * 这里简化一下，就传一个自定义参数对象，然后采用fastjon序列化。
 **/

public class RemotingCommand {

    private static final AtomicInteger requestId = new AtomicInteger(0);
    //请求唯一ID，服务端与客户端会通过这个commandId来匹配
    private int commandId;
    // 请求码。
    private int code;
    // 请求类型：0 requestRPC;1 onewayRPC;2 responseRPC;
    private int flag=0;
    // message字段主要用于ResponseCommand，包含一段提示把。
    // body内容
    private String message = "";
    // 这个param就是业务参数对象。在RequestCommand中一般必传。 ResponseCommand中选传。
    //业务参数
    private CustomerCommandParam customerCommandParam;


    public static RemotingCommand createResponseCommand(int code, String remark) {
        RemotingCommand command = new RemotingCommand();
        command.setCode(code);
        command.markResponseType();
        command.setMessage(remark);
        return command;
    }

    public static RemotingCommand createRequestCommand(int code ,CustomerCommandParam param){
        RemotingCommand command = new RemotingCommand();
        command.setCode(code);
        command.setCustomerCommandParam(param);
        return command;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public int getMessageLength() {
        return message.getBytes(StandardCharsets.UTF_8).length;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getCommandId() {
        return commandId;
    }
    public void setCommandId(int commandId) {
        this.commandId = commandId;
    }

    public void initCommandId(){
        this.commandId = requestId.getAndIncrement();
    }

    @Override
    public String toString() {
        return "RemotingCommand{" +
                "commandId=" + commandId +
                ", code=" + code +
                ", flag=" + flag +
                ", message='" + message + '\'' +
                ", customerCommandParam=" + customerCommandParam +
                '}';
    }

    public void encodeInto(ByteBuf byteBuf){
        byteBuf.writeInt(this.getCommandId());
        byteBuf.writeInt(this.getCode());
        byteBuf.writeInt(this.flag);
        final int messagelength = this.getMessageLength();
        byteBuf.writeInt(messagelength);
        if(messagelength > 0){
            byteBuf.writeBytes(this.getMessage().getBytes(StandardCharsets.UTF_8));
        }

        if(null == customerCommandParam){
            byteBuf.writeInt(0);
        }else{
            final String paramString = JSON.toJSONString(customerCommandParam);
            byteBuf.writeInt(paramString.getBytes(StandardCharsets.UTF_8).length);
            byteBuf.writeBytes(paramString.getBytes(StandardCharsets.UTF_8));
        }
    }

    public static RemotingCommand decodeFrom(ByteBuf byteBuf){
        RemotingCommand command = new RemotingCommand();

        int commandId = byteBuf.readInt();
        final int code = byteBuf.readInt();
        final int flag = byteBuf.readInt();
        final int requestLength = byteBuf.readInt();

        command.setCommandId(commandId);
        command.setCode(code);
        command.setFlag(flag);
        if(requestLength > 0){
            command.setMessage(byteBuf.readBytes(requestLength).toString(StandardCharsets.UTF_8));
        }

        final int paramLength = byteBuf.readInt();
        if(paramLength > 0){
            final String paramStr = byteBuf.readBytes(paramLength).toString(StandardCharsets.UTF_8);
            final CustomerCommandParam customerCommandParam = JSON.parseObject(paramStr, CustomerCommandParam.class);
            command.setCustomerCommandParam(customerCommandParam);
        }
        return command;
    }

    public void markOnewayRPC(){
        this.flag = 1;
    }

    public Boolean isOnewayRPC(){
        return this.flag==1;
    }
    public void markResponseType(){
        this.flag = 2 ;
    }

    public Boolean isResponseType(){
        return this.flag ==2;
    }
    public void setFlag(int flag){
        this.flag = flag;
    }
    public CustomerCommandParam getCustomerCommandParam() {
        return customerCommandParam;
    }

    public void setCustomerCommandParam(CustomerCommandParam customerCommandParam) {
        this.customerCommandParam = customerCommandParam;
    }
}
